package com.lucas.pojo;


import com.lucas.handler.UserClientHandler;
import com.lucsa.jsonUtils.jsonUtil;
import com.lucsa.pojo.RpcRequest;
import com.lucsa.utils.RpcDecoder;
import com.lucsa.utils.RpcEncoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 远程调用者
 *
 * 思路:
 * 客户端调用代理方法，返回一个实现了 HelloService 接口的代理对象，调用代理对象的方法，返回结果。
 * 我们需要在代理中做手脚，当调用代理方法的时候，我们需要初始化 Netty 客户端，还需要向服务端请求数据，并
 * 返回数据。
 *
 *
 * 要求完成改造版本：
 *
 *  序列化协议修改为JSON，使用fastjson作为JSON框架，并根据RpcRequest实体作为通信协议，服务端需根据客户端传递过来的RpcRequest对象通过反射，动态代理等技术，最终能够执行目标方法，返回字符串"success"
 */
public class RpcConsumer {

    //构建线程池
    private  ExecutorService executor =
            Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    //客户的处理类
    public  UserClientHandler userClientHandler;

    //创建代理对象,把客户端知道的rpcClzz放入.然后通过创建代理对象获得了可调用的远程对象
    public  Object creatProxy(Class<?> clzz,String ip,String port ){

        //
      return   Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{clzz}, new InvocationHandler() {
          //每次调用sayHello 实际就是调用这个invoke方法
          @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if(userClientHandler==null){
                    initClient(ip,port);
                }
                //设置参数意义,param表示要发送的信息给客户.以及providerName发送到那个一个服务端上的暴露的服务.
                userClientHandler.setRpcRequest((RpcRequest) args[0]);
              /**
               * 每次调用它的方法,就是会产生线程,利用线程去调用.
               * executor.sumbit(userClientHandler) 表示使用线程,触发call()方法
               * .get()表示获取call方法之后返回值
               */

                return  executor.submit(userClientHandler).get();
            }
        });
    }


    public  void initClient(String ip,String port){
        userClientHandler = new UserClientHandler();

        try {
            //设置eventLoopGroup
            NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();

            Bootstrap bootstrap = new Bootstrap();

            bootstrap.group(eventLoopGroup)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
//                            pipeline.addLast(new StringEncoder());
//                            pipeline.addLast(new StringDecoder());
//                            pipeline.addLast(userClientHandler);
                            pipeline.addLast(new RpcEncoder(RpcRequest.class,new jsonUtil()));
                            pipeline.addLast(new RpcDecoder(RpcRequest.class,new jsonUtil()));
//                            userClientHandler.setRpcRequest();
                            pipeline.addLast(userClientHandler);

                        }
                    });
            Integer portInt = Integer.valueOf(port);
            bootstrap.connect(ip,portInt).sync();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
